﻿
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using Dexter.CallContext;
using Dexter.Dependency;
using Dexter.Logger.Contracts;
using Dexter.PluginManager;
using Dexter.Scheduler;
using Dexter.Services;
using Dexter.Web.Routing;
using MvcContrib.UI.InputBuilder;

namespace Dexter.Web.Mvc {
	public class DexterApplication : IDexterApplication{
		readonly IDexterContainer container;
		readonly ILogger logger;
		readonly IRoutingService routingService;
		readonly ISetupService setupService;
		readonly IPluginManager pluginManager;
		readonly IDexterCall dexterCall;
		readonly IScheduler scheduler;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:System.Object"/> class.
		/// </summary>
		/// <param name="container">The container.</param>
		/// <param name="logger">The logger.</param>
		/// <param name="routingService">The routing service.</param>
		/// <param name="setupService">The setup service.</param>
		public DexterApplication ( IDexterContainer container , ILogger logger , IRoutingService routingService , ISetupService setupService, IPluginManager pluginManager, IDexterCall dexterCall, IScheduler scheduler) {
			this.container = container;
			this.logger = logger;
			this.routingService = routingService;
			this.setupService = setupService;
			this.pluginManager = pluginManager;
			this.dexterCall = dexterCall;
			this.scheduler = scheduler;
		}

		public void Init ( HttpApplication application ) {
			var modules = container.ResolveAll<IHttpModule> ( );

			foreach (var httpModule in modules) {
				httpModule.Init ( application );
			}
		}

		public void ApplicationStart ( ) {
			Regex.CacheSize = 50;

			routingService.RegisterRoutes ( );

			if (setupService.IsInstalled) {
				RegisterGlobalFilters ( GlobalFilters.Filters );
				RegisterViewEngineAndTheme ( System.Web.Mvc.ViewEngines.Engines );

				pluginManager.LoadPlugins();
				pluginManager.EnablePlugins();

				scheduler.StartScheduler(TimeSpan.FromSeconds(10));

				InputBuilder.BootStrap();
			}

			RegisterControllers ( );
			RegisterVirtualPathrovider ( );

			//It's needed beccause the startup procedure could be read/write into the database and the session is not managed by the filer
			if (dexterCall != null)
				dexterCall.Complete(true);
		}

		public void ApplicationEnd ( ) {
			var runtime = ( HttpRuntime )typeof ( HttpRuntime ).InvokeMember ( "_theRuntime", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null );

			if (runtime != null) {
				var shutDownMessage = ( string )runtime.GetType ( ).InvokeMember ( "_shutDownMessage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null );
				var shutDownStack = ( string )runtime.GetType ( ).InvokeMember ( "_shutDownStack", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null );

				if (shutDownMessage.Contains ( "HostingEnvironment initiated shutdown" )) {
					//The normal app domain recycle should be logged as a info
					logger.InfoFormat ( "{0} stack = {1}", shutDownMessage, shutDownStack );
				}
				else {
					//maybe there is a problem :)
					logger.WarnFormat ( "{0} stack = {1}", shutDownMessage, shutDownStack );
				}
			}

			logger.Info ( "Application shutdown" );
			container.Shutdown ( );
		}

		public void ApplicationError ( ) {
			var call = container.Resolve<IDexterCall>();

			if(call != null)
				call.Complete(false);
		}

		public void AuthenticateRequest ( ) { }

		void RegisterViewEngineAndTheme ( ViewEngineCollection engines ) {
			engines.Clear ( );
			var viewEngines = container.ResolveAll<IViewEngine> ( );
			viewEngines.ForEach ( engines.Add );
		}

		void RegisterGlobalFilters ( GlobalFilterCollection filters ) {
			var f = container.ResolveAll <FilterAttribute> ( );

			f.ForEach ( filters.Add );
		}

		void RegisterControllers ( ) {
			ControllerBuilder.Current.SetControllerFactory ( container.Resolve<IControllerFactory> ( ) );
		}

		void RegisterVirtualPathrovider() {
			var vpp = container.Resolve <VirtualPathProvider> ( );

			if(vpp != null)
				System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider ( vpp );
		}
	}
}
